home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
programr
/
utexec2.zip
/
README.TXT
next >
Wrap
Text File
|
1994-07-12
|
8KB
|
182 lines
Disclaimer:
The mind boggles thinking how intelligent the
programmer's at Microsoft must be. How can I, a
lowly mortal with no stock options, understand the
awe inspiring intellect of those whose boots I am
unworthy to lick? They must have wisdom that far
outstrips mine, since they graced us with the
inability to run a program and wait for it to finish.
How my heart aches to reach even the foothills of
their mountainous insight, the heights of which I am
unable to see. Oh Great Microsoft! What plan could
the High Microsoft Priests be leading us towards that
I am blinded to by my ignorance? Here am I,
floundering with small pieces of a puzzle that I
surely could never comprehend. Oh great Bill! Oh
great denizens of Microsoft! I am filled with joyous
rapture knowing your awe inspiring gray matter
permeates my universe! I hope that I, ignorant
heathen that I am, do not force programmer's to stray
from the Holy Plan Of Microsoft. I hope that I,
ignorant heathen that I am, do not force
programmer's off the One True Way, the way lead by
the Omniscient Bill! I pray to Redmond that my
feeble attempts will please the great Bill!
Documentation:
Under good old Windows all that has to be done to
execute a program and wait for it to terminate was a
WinExec. We would then either play with the
toolhelp DLL, or enumerate through all of the
windows in the system to find the one whose
instance handle we where given. This information
can be used to spin lock on the window disappearing
or the task that the window represents terminating.
This method works for both 16bit Windows
programs and 32bit Windows programs when using
Watcom's 32bit window's compiler. Watcom has
done a much better job than Microsoft's single entry
approach via the Universal Thunk interface, by the
way.
When under NT, a call to CreateProcess and
WaitForSingleObject is all that is needed. The only
slightly slippery problem occurs when CreateProcess
starts up an application that requires NT to load
another subsystem. You are given what is called a
pseudo handle instead of a normal process handle.
There is not much you can do with a pseudo handle,
but you can WaitForSingleObject on it. Praise be to
the Glory of Microsoft that they added this
capability.
When under WIN32S, you can only use
CreateProcess to start up other 32bit Windows
programs. 16bit Windows programs just will not
start. Do not bother looking at the error codes to see
what happened, because they indicate that everything
worked just fine. Even if you could reliably use
CreateProcess, WaitForSingleObject does not exist
under WIN32S. You could possibly try using
GetExitCodeProcess, but once again this works for
32bit programs but does not work on 16bit ones. The
only thing you can do to consistently start another
program is WinExec. Unfortunately the return value
from WinExec is practically meaningless. Under
normal Windows, this is the instance handle of the
program that you just started if it is greater than 32,
otherwise it is an error code. Under WIN32S the
value returned can still be one of those 32 error
codes, but any successful invocation returns 33. This
gives us the ability to launch programs without the
ability of knowing exactly what program we just
launched.
This leaves us with only one way to successfully
launch a program and get any information back from
the launch. It has to be done under 16bit Windows.
Consequently, we have to invoke the Universal
Thunk in order to start up a 16bit DLL that will do
all of the dirty work for us. At this point you can use
one of the previously mentioned Windows methods for waiting
on a program to terminate. I decided that this 16bit
DLL would 1) Start up the program and retrieve it's
instance handle. 2) From that instance handle, find
the appropriate window handle. 3) Keep track of that
window handle and notify us when it or it's
associated task has terminated. This seems more
straight forward that using the toolhelp DLL
functions.
I will not go into the gory details of how to invoke
Universal Thunks, as there are better articles on that
process elsewhere. I will explain the interface to this
particular 16bit DLL, and suffice it to say that the
goofy parameter passing stuff is a requirement when
communicating through the Universal Thunk
interface. The only strange thing is the requirement
for the calling routine to allocate a chunk of space
that the DLL uses to store all of its data. This
requirement allows the DLL to be reentrant, and
subsequently allow access to it by multi-threaded
programs.
The first entry point of the 16bit DLL returns the
size of the data area that the calling program needs
to allocate. This space will be used by the DLL to
store any relevant data. The second entry point uses
this data storage space, and a string that the DLL
will try to execute. The standard Universal Thunk
pointer nonsense has to be done. It is important to
note that pointers are translated in place, which
would preclude their use after any call through the
Universal Thunk. The third entry point tells the
calling program if the application that it started is
still running. Using these three entry points, you can
start up any program under WIN32S and wait for it
to terminate. The actual code that does all of this is
located in the file 'run32.c.' All of the information
that you need to interface with Universal Thunks in
general and the run16 DLL in particular is located in
this file.
The run16 DLL was built using Visual C++ under
the large memory model, but it is a pretty generic
DLL that can be built using any decent compiler
tool. The only thing of note is that any routine that is
exported out of any DLL must have the __export
keyword, along with the __pascal calling
convention. Mystery lockups will happen if either of
those requirements are ignored. All of the code for
the DLL is in the file 'run16.c' and it's accompanying
'run16.def' file.
The run32 DLL was also built using Visual C++.
Note that the memory allocation call was done using
the often maligned GlobalAlloc. If a call to the
standard C runtime library malloc was used,
various invocations of a mystery function called
_CRT_INIT would have to be added to the DLL's
entry point in order to initialize the C runtime
library. Adding these calls also eliminates the undocumented
linker error that results. The run32 DLL can be
invoked from any 32bit program. A simple call to
the TryExec entry point sets all of the mysterious
wheels of Universal Thunks in motion. It is
generally a good idea to place any invocation of the
Universal Thunk nonsense in a separate DLL. Not
only does it hide it's rather bizarre invocation, it also
allows the use of multiple Universal Thunks. Buried
in the bowels of the Universal Thunk documentation
is the rather curious fact that only one Universal
Thunk can exist for any EXE or DLL. This is
another shining example of Band-Aid programming,
but one that is easily covered worked around.
Both files need to have access to the WIN32S
Universal Thunk .h and .lib files when they are built.
It is my hope that by donating this code to the public
domain, people will use it as an example of how to
execute and wait for a program under WIN32S. If
this code saves another programmer from the time it
took me to find ANY solution, then I will be happy. I
hereby release all rights to this code and any derived
work, do not sue me, etc, etc, #include
<std.disclaimer>. I have also included the compiled
DLL's, in case anyone does not have access to a C
compiler. This should not imply that these files are
production quality code. I would hope that anyone
that really wants to use these routines would add a
bit more error checking...
In the name of Bill,
Timothy A. Anderson
CI$ 72657,3330
PO Box 6659
Beaverton, OR 97007